﻿using Newtonsoft.Json;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using OpcUA.Model;

namespace OpcUA
{
    /// <summary>
    /// 操作接口
    /// </summary>
    public class Manager
    {
        public string PointName;
        public string Url = "";

        private EventHandler<ReturnChangeDataList> m_OpcChangeDataEvent;
        private EventHandler<ChangeStatusData> m_OpcChangeStatusEvent;
        private UAClient uaClient = null;
        private object websocketsession;

        #region

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="session">WebSocket传输时使用，生产情况下传NULL</param>
        public Manager(object session)
        {
            this.websocketsession = session;
        }

        /// <summary>
        /// 已订阅数据变化事件
        /// </summary>
        public event EventHandler<ReturnChangeDataList> M_OpcChangeDataEvent
        {
            add { m_OpcChangeDataEvent += value; }
            remove { m_OpcChangeDataEvent -= value; }
        }

        public event EventHandler<ChangeStatusData> M_OpcChangeStatusEvent
        {
            add { m_OpcChangeStatusEvent += value; }
            remove { m_OpcChangeStatusEvent -= value; }
        }

        /// <summary>
        /// 带返回json的key组订阅,生产端使用
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tags"></param>
        /// <returns></returns>
        public string AddAbnormityNodeIdsSub(string key, string[] tags)
        {
            if (uaClient.AddAbnormitySubscriptionForKeyGroup(websocketsession, key, tags, ABSubCallBack))
            {
                return JsonConvert.SerializeObject(GetJsonData("NodeSub", true, ""));
            }
            else
            {
                return JsonConvert.SerializeObject(GetJsonData("NodeSub", false, ""));
            }
        }

        /// <summary>
        /// 带返回json的key组订阅,生产端使用
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tags"></param>
        /// <returns></returns>
        public string AddNodeIdsSub(string key, string[] tags, int Proctype, int Inverval)
        {
            if (uaClient.AddSubscriptionForKeyGroup(websocketsession, key, tags, Proctype, Inverval, SubCallBack))
            {
                return JsonConvert.SerializeObject(GetJsonData("NodeSub", true, ""));
            }
            else
            {
                return JsonConvert.SerializeObject(GetJsonData("NodeSub", false, ""));
            }
        }

        /// <summary>
        /// Close
        /// </summary>
        public void CloseClient()
        {
            uaClient.Disconnect();
        }

        /// <summary>
        /// 将字典类型转换为对象类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dictionary"></param>
        /// <returns></returns>
        public T ConvertDictionaryToObject<T>(Dictionary<string, object> dictionary) where T : new()
        {
            T obj = new T();
            var data = dictionary.ToList();
            if (data != null)
            {
                foreach (var kvp in data)
                {
                    //更具键名获取实体类属性名称
                    var property = typeof(T).GetProperty(kvp.Key);
                    //var convertedValue = kvp.Value;
                    //数据类型判断
                    if (property != null && kvp.Value != null)
                    {
                        var targetType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
                        var convertedValue = kvp.Value;
                        property.SetValue(obj, convertedValue);
                    }
                }
            }
            return obj;

        }

        /// <summary>
        ///连接为无或证书
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task<string> CreateOpcUa(string url)
        {
            try
            {
                IOutput console = new ConsoleOutput();
                //定义UA客户端应用
                ApplicationInstance application = new ApplicationInstance();
                application.ApplicationName = "Quickstart AutoMaterialsClient";
                application.ApplicationType = ApplicationType.Client;

                // 加载应用程序配置。
                await application.LoadApplicationConfiguration(AppDomain.CurrentDomain.BaseDirectory + "\\MaterialsClient.Config.xml", silent: false);
                // 检查申请证书。
                await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0);

                //创建UA客户端对象，连接已配置的服务器。
                uaClient = new UAClient(application.ApplicationConfiguration, console, ClientBase.ValidateResponse);
                uaClient.ServerUrl = url;
                uaClient.UserIdentity = new UserIdentity();
                bool connected = await uaClient.ConnectAsync();
                if (connected)
                {
                    return JsonConvert.SerializeObject(GetJsonData("Connect", true, "已连接到服务端！"));
                }
                else
                {
                    return JsonConvert.SerializeObject(GetJsonData("Connect", false, "无法连接到服务端！"));
                }
            }
            catch (Exception ex)
            {
                return JsonConvert.SerializeObject(GetJsonData("Connect", false, ex.Message));
            }
        }

        /// <summary>
        /// 帐号连接
        /// </summary>
        /// <param name="url"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<string> CreateOpcUa(string url, string username, string password)
        {
            try
            {
                IOutput console = new ConsoleOutput();
                // Define the UA Client application
                ApplicationInstance application = new ApplicationInstance();
                application.ApplicationName = "Quickstart AutoMaticClient";
                application.ApplicationType = ApplicationType.Client;

                // load the application configuration.
                await application.LoadApplicationConfiguration(AppDomain.CurrentDomain.BaseDirectory + "\\AutoMaticClient.Config.xml", silent: false);
                // check the application certificate.
                await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0);

                // create the UA Client object and connect to configured server.
                uaClient = new UAClient(application.ApplicationConfiguration, console, ClientBase.ValidateResponse);
                uaClient.ServerUrl = url;
                uaClient.UserIdentity = new UserIdentity(username, password);
                //uaClient.OpcStatusChange += UaClient_OpcStatusChange;
                //uaClient.ReconnectStarting += UaClient_ReconnectStarting;
                //uaClient.ReconnectComplete += UaClient_ReconnectComplete;
                bool connected = await uaClient.ConnectAsync();
                if (connected)
                {
                    return JsonConvert.SerializeObject(GetJsonData("Connect", true, "已连接到服务端！"));
                }
                else
                {
                    return JsonConvert.SerializeObject(GetJsonData("Connect", false, "无法连接到服务端！"));
                }
            }
            catch (Exception ex)
            {
                return JsonConvert.SerializeObject(GetJsonData("Connect", false, ex.Message));
            }
        }

        /// <summary>
        /// 每秒读取
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task<string> CreateUa(string url)
        {
            try
            {
                IOutput console = new ConsoleOutput();
                // Define the UA Client application
                ApplicationInstance application = new ApplicationInstance();
                application.ApplicationName = "Quickstart AutoMaterialsClient";
                application.ApplicationType = ApplicationType.Client;

                //定义UA客户端应用
                await application.LoadApplicationConfiguration(AppDomain.CurrentDomain.BaseDirectory + "MaterialsClient.Config.xml", silent: false);
                // 检查申请证书。
                await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0);

                //创建UA客户端对象，连接已配置的服务器。
                UAClient uaClient1 = new UAClient(application.ApplicationConfiguration, console, ClientBase.ValidateResponse);
                uaClient1.ServerUrl = url;
                uaClient1.UserIdentity = new UserIdentity();
                bool connected = await uaClient.ConnectAsync();
                if (connected)
                {
                    return JsonConvert.SerializeObject(GetJsonData("Connect", true, "已连接到服务端！"));
                }
                else
                {
                    return JsonConvert.SerializeObject(GetJsonData("Connect", false, "无法连接到服务端！"));
                }
            }
            catch (Exception ex)
            {
                return JsonConvert.SerializeObject(GetJsonData("Connect", false, ex.Message));
            }
        }

        /// <summary>
        /// ShowNodeMember
        /// </summary>
        /// <param name="NodeID"></param>
        /// <returns></returns>
        public string GetChidMemberShow(string NodeID)
        {
            int index = 0;
            try
            {
                NodeChildMember NM = new NodeChildMember();
                NM.PNodeID = NodeID;
                ReferenceDescriptionCollection references;
                references = uaClient.GetReferenceDescriptionCollection((NodeId)NodeID);

                List<NodeData> List = new List<NodeData>();
                if (references?.Count > 0)
                {
                    // 获取所有要读取的子节点
                    List<NodeId> nodeIds = new List<NodeId>();
                    for (int ii = 0; ii < references.Count; ii++)
                    {
                        ReferenceDescription target = references[ii];
                        nodeIds.Add((NodeId)target.NodeId);
                    }

                    //DateTime dateTimeStart = DateTime.Now;

                    // 获取所有的值
                    DataValue[] dataValues = uaClient.ReadOneNodeFiveAttributes(nodeIds);
                    //int timespan = (int)(DateTime.Now - dateTimeStart).TotalMilliseconds + " ms";

                    // 显示
                    for (int jj = 0; jj < dataValues.Length; jj += 5)
                    {
                        List.Add(AddDataNewRow(dataValues, jj, index++, nodeIds[jj / 5]));
                    }
                    NM.List = List;
                    return JsonConvert.SerializeObject(GetJsonData("NodeShow", true, JsonConvert.SerializeObject(NM)));
                }
                else
                {
                    // 子节点没有数据的情况
                    DataValue dataValue = uaClient.ReadNode((NodeId)NodeID);

                    if (dataValue.WrappedValue.TypeInfo?.ValueRank == ValueRanks.OneDimension)
                    {
                        // 数组显示
                        List.AddRange(AddDataArrayRow((NodeId)NodeID, out index));
                    }
                    else
                    {
                        List.Add(AddDataNewRow(uaClient.ReadOneNodeFiveAttributes(new List<NodeId>() { (NodeId)NodeID }), 0, index++, (NodeId)NodeID));
                    }
                    NM.List = List;
                    return JsonConvert.SerializeObject(GetJsonData("NodeShow", true, JsonConvert.SerializeObject(NM)));
                }
            }
            catch (Exception ex)
            {
                return JsonConvert.SerializeObject(GetJsonData("NodeShow", false, ex.Message));
            }
        }

        /// <summary>
        /// 订阅多个Node,Web使用，key默认为NormalSub
        /// </summary>
        /// <param name="subNodeIds"></param>
        /// <returns></returns>
        public string GetChildMembersSub(string[] subNodeIds)
        {
            SubScriptionNode("normalsub", subNodeIds.ToArray());
            return JsonConvert.SerializeObject(GetJsonData("NodeSub", true, ""));
        }

        /// <summary>
        /// 订阅NodeID下的子节点
        /// </summary>
        /// <param name="NodeID"></param>
        /// <returns></returns>
        public List<string> GetChildMemberSub(string NodeID)
        {
            //try
            //{
            ReferenceDescriptionCollection references;
            references = uaClient.GetReferenceDescriptionCollection(new NodeId(NodeID));

            List<string> subNodeIds = new List<string>();
            if (references?.Count > 0)
            {
                // 获取所有要订阅的子节点
                for (int ii = 0; ii < references.Count; ii++)
                {
                    ReferenceDescription target = references[ii];
                    subNodeIds.Add(((NodeId)target.NodeId).ToString());
                }
            }
            else
            {
                // 子节点没有数据的情况
                subNodeIds.Add(NodeID);
            }

            //SubScriptionNode("normalsub", subNodeIds.ToArray());
            return subNodeIds;
            //}
            //catch (Exception exception)
            //{
            //  return subNodeIds;
            //}
        }

        /// <summary>
        /// 获取所有子节点
        /// </summary>
        /// <returns></returns>
        public string GetChildNode(string NodeID)
        {
            return GetChildNodeByParentNode(NodeID, "NodeExpand");
        }

        /// <summary>
        /// ExpandNodeForNodeID
        /// </summary>
        /// <param name="NodeID"></param>
        /// <returns></returns>
        public string GetChildNodeByParentNode(string NodeID, string NodeAction)
        {
            try
            {
                ReferenceDescriptionCollection RDC = uaClient.GetReferenceDescriptionCollection((NodeId)NodeID);
                List<ReturnTreeNode> listtreenode = new List<ReturnTreeNode>();
                foreach (ReferenceDescription result in RDC)
                {
                    bool canExpand = false;
                    if (uaClient.GetReferenceDescriptionCollection((NodeId)result.NodeId).Count > 0)
                    {
                        canExpand = true;
                    }
                    listtreenode.Add(new ReturnTreeNode() { pId = NodeID, id = result.NodeId.ToString(), name = Utils.Format("{0}", result), icon = "img/" + GetImageKeyFromDescription(result, (NodeId)NodeID) + ".png", isParent = canExpand });
                }
                return JsonConvert.SerializeObject(GetJsonData(NodeAction, true, JsonConvert.SerializeObject(listtreenode)));
            }
            catch (Exception ex)
            {
                return JsonConvert.SerializeObject(GetJsonData(NodeAction, false, ex.Message));
            }
        }

        /// <summary>
        /// 获取所有父节点
        /// </summary>
        /// <returns></returns>
        public string GetParentNode()
        {
            return GetChildNodeByParentNode(ObjectIds.ObjectsFolder.ToString(), "Folder");
        }



        /// <summary>
        /// 移除订阅
        /// </summary>
        public void RemoveAllSubscription()
        {
            uaClient.RemoveAllSubscription();
        }

        /// <summary>
        /// Write Multi Values
        /// </summary>
        /// <param name="tags"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public string WriteNodes(string[] tags, string[] values)
        {
            try
            {
                if (uaClient.WriteNodes(tags, values))
                {
                    return JsonConvert.SerializeObject(GetJsonData("WriteData", true, ""));
                }
                else
                {
                    return JsonConvert.SerializeObject(GetJsonData("WriteData", false, ""));
                }
            }
            catch (Exception ex)
            {
                return JsonConvert.SerializeObject(GetJsonData("WriteData", false, ex.Message));
            }
        }

        /// <summary>
        /// ABCallBack
        /// </summary>
        /// <param name="session"></param>
        /// <param name="key"></param>
        /// <param name="monitoredItem"></param>
        /// <param name="eventArgs"></param>
        private void ABSubCallBack(object session, string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs eventArgs)
        {
            ReturnChangeDataList RCD = new ReturnChangeDataList();

            MonitoredItemNotification notification = eventArgs.NotificationValue as MonitoredItemNotification;
            if (notification != null)
            {
                List<OpcChangeData> List = new List<OpcChangeData>();
                ChangeData cd = new ChangeData();
                RCD.session = session;
                RCD.key = key;
                cd.nodeid = monitoredItem.StartNodeId.ToString();
                if (notification.Value.WrappedValue.TypeInfo?.ValueRank == ValueRanks.OneDimension)
                {
                    Array array = notification.Value.WrappedValue.Value as Array;
                    int i = 0;

                    foreach (object obj in array)
                    {
                        OpcChangeData ocd = new OpcChangeData();
                        ocd.value = obj == null ? "" : obj.ToString();
                        List.Add(ocd);
                        i++;
                    }
                }
                else
                {
                    OpcChangeData ocd = new OpcChangeData();
                    ocd.value = notification.Value.WrappedValue.Value == null ? "" : notification.Value.WrappedValue.Value.ToString();
                    List.Add(ocd);
                }
                cd.List = List;
                RCD.ChangeData = cd;
                m_OpcChangeDataEvent?.Invoke(this, RCD);
            }
        }

        /// <summary>
        /// GetRowNode
        /// </summary>
        /// <param name="nodeId"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private List<NodeData> AddDataArrayRow(NodeId nodeId, out int index)
        {
            List<NodeData> List = new List<NodeData>();
            DataValue[] dataValues = uaClient.ReadOneNodeFiveAttributes(new List<NodeId>() { nodeId });
            DataValue dataValue = dataValues[1];

            if (dataValue.WrappedValue.TypeInfo?.ValueRank == ValueRanks.OneDimension)
            {
                string access = GetDiscriptionFromAccessLevel(dataValues[2]);
                BuiltInType type = dataValue.WrappedValue.TypeInfo.BuiltInType;
                object des = dataValues[4].Value ?? "";
                object dis = dataValues[3].Value ?? type;

                Array array = dataValue.Value as Array;
                int i = 0;
                foreach (object obj in array)
                {
                    NodeData nd = new NodeData();
                    nd.classkey = "Enum_582";
                    nd.name = $"{dis} [{i++}]";
                    nd.value = obj.ToString();
                    nd.type = type.ToString();
                    nd.level = access.ToString();
                    nd.description = des.ToString();
                    nd.nodeid = nodeId.ToString();
                    List.Add(nd);
                }
                index = i;
            }
            else
            {
                index = 0;
            }

            return List;
        }

        /// <summary>
        /// GetChideNode
        /// </summary>
        /// <param name="dataValues"></param>
        /// <param name="startIndex"></param>
        /// <param name="index"></param>
        /// <param name="nodeId"></param>
        /// <returns></returns>
        private NodeData AddDataNewRow(DataValue[] dataValues, int startIndex, int index, NodeId nodeId)
        {
            NodeData nd = new NodeData();
            if (dataValues[startIndex].WrappedValue.Value == null) return nd;
            NodeClass nodeclass = (NodeClass)dataValues[startIndex].WrappedValue.Value;
            nd.name = dataValues[3 + startIndex].WrappedValue.Value == null ? "" : dataValues[3 + startIndex].WrappedValue.Value.ToString();
            nd.description = dataValues[4 + startIndex].WrappedValue.Value == null ? "" : dataValues[4 + startIndex].WrappedValue.Value.ToString();
            nd.level = GetDiscriptionFromAccessLevel(dataValues[2 + startIndex]);
            if (nodeclass == NodeClass.Object)
            {
                nd.value = "";
                nd.type = nodeclass.ToString();
                nd.classkey = "ClassIcon";
            }
            else if (nodeclass == NodeClass.Method)
            {
                nd.value = "";
                nd.type = nodeclass.ToString();
                nd.classkey = "Method_636";
            }
            else if (nodeclass == NodeClass.Variable)
            {
                DataValue dataValue = dataValues[1 + startIndex];

                if (dataValue.WrappedValue.TypeInfo != null)
                {
                    nd.type = dataValue.WrappedValue.TypeInfo.BuiltInType.ToString();
                    //dgvr.Cells[3].Value = dataValue.WrappedValue.TypeInfo.BuiltInType;
                    // dgvr.Cells[3].Value = dataValue.Value.GetType().ToString();
                    if (dataValue.WrappedValue.TypeInfo.ValueRank == ValueRanks.Scalar)
                    {
                        nd.value = dataValue.WrappedValue.Value.ToString();
                        nd.classkey = "Enum_582";
                    }
                    else if (dataValue.WrappedValue.TypeInfo.ValueRank == ValueRanks.OneDimension)
                    {
                        nd.value = dataValue.Value.GetType().ToString();
                        nd.classkey = "brackets";
                    }
                    else if (dataValue.WrappedValue.TypeInfo.ValueRank == ValueRanks.TwoDimensions)
                    {
                        nd.value = dataValue.Value.GetType().ToString();
                        nd.classkey = "Module_648";
                    }
                    else
                    {
                        nd.value = dataValue.Value.GetType().ToString();
                        nd.classkey = "ClassIcon";
                    }
                }
                else
                {
                    nd.classkey = "ClassIcon";
                    nd.value = dataValue.Value == null ? "null" : dataValue.Value.ToString();
                    nd.type = "null";
                }
            }
            else
            {
                nd.value = "";
                nd.classkey = "ClassIcon";
                nd.type = nodeclass.ToString();
            }
            nd.nodeid = nodeId.ToString();
            return nd;
        }

        /// <summary>
        /// GetLevel
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private string GetDiscriptionFromAccessLevel(DataValue value)
        {
            if (value.WrappedValue.Value != null)
            {
                switch ((byte)value.WrappedValue.Value)
                {
                    case 0: return "None";
                    case 1: return "CurrentRead";
                    case 2: return "CurrentWrite";
                    case 3: return "CurrentReadOrWrite";
                    case 4: return "HistoryRead";
                    case 8: return "HistoryWrite";
                    case 12: return "HistoryReadOrWrite";
                    case 16: return "SemanticChange";
                    case 32: return "StatusWrite";
                    case 64: return "TimestampWrite";
                    default: return "None";
                }
            }
            else
            {
                return "null";
            }
        }

        /// <summary>
        /// GetClassKey
        /// </summary>
        /// <param name="target"></param>
        /// <param name="sourceId"></param>
        /// <returns></returns>
        private string GetImageKeyFromDescription(ReferenceDescription target, NodeId sourceId)
        {
            if (target.NodeClass == NodeClass.Variable)
            {
                DataValue dataValue = uaClient.ReadNode((NodeId)target.NodeId);

                if (dataValue.WrappedValue.TypeInfo != null)
                {
                    if (dataValue.WrappedValue.TypeInfo.ValueRank == ValueRanks.Scalar)
                    {
                        return "Enum_582";
                    }
                    else if (dataValue.WrappedValue.TypeInfo.ValueRank == ValueRanks.OneDimension)
                    {
                        return "brackets";
                    }
                    else if (dataValue.WrappedValue.TypeInfo.ValueRank == ValueRanks.TwoDimensions)
                    {
                        return "Module_648";
                    }
                    else
                    {
                        return "ClassIcon";
                    }
                }
                else
                {
                    return "ClassIcon";
                }
            }
            else if (target.NodeClass == NodeClass.Object)
            {
                if (sourceId == ObjectIds.ObjectsFolder)
                {
                    return "VirtualMachine";
                }
                else
                {
                    return "ClassIcon";
                }
            }
            else if (target.NodeClass == NodeClass.Method)
            {
                return "Method_636";
            }
            else
            {
                return "ClassIcon";
            }
        }

        /// <summary>
        /// GetJsonData
        /// </summary>
        /// <param name="type"></param>
        /// <param name="success"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        private ReturnJsonMessage GetJsonData(string type, bool success, string msg)
        {
            return new ReturnJsonMessage() { MessageType = type, ReturnMessage = new ReturnMessage() { Result = success, Message = msg } };
        }

        /// <summary>
        /// CallBack
        /// </summary>
        /// <param name="session"></param>
        /// <param name="key"></param>
        /// <param name="monitoredItem"></param>
        /// <param name="eventArgs"></param>
        private void SubCallBack(object session, string key, int proctype, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs eventArgs)
        {
            ReturnChangeDataList RCD = new ReturnChangeDataList();

            MonitoredItemNotification notification = eventArgs.NotificationValue as MonitoredItemNotification;
            if (notification != null)
            {
                List<OpcChangeData> List = new List<OpcChangeData>();
                ChangeData cd = new ChangeData();
                RCD.session = session;
                RCD.key = key;
                RCD.procType = proctype;
                cd.nodeid = monitoredItem.StartNodeId.ToString();
                if (notification.Value.WrappedValue.TypeInfo?.ValueRank == ValueRanks.OneDimension)
                {
                    Array array = notification.Value.WrappedValue.Value as Array;
                    int i = 0;

                    foreach (object obj in array)
                    {
                        OpcChangeData ocd = new OpcChangeData();
                        ocd.value = obj == null ? "" : obj.ToString();
                        List.Add(ocd);
                        i++;
                    }
                }
                else
                {
                    OpcChangeData ocd = new OpcChangeData();
                    ocd.value = notification.Value.WrappedValue.Value == null ? "" : notification.Value.WrappedValue.Value.ToString();
                    List.Add(ocd);
                }
                cd.List = List;
                RCD.ChangeData = cd;
                m_OpcChangeDataEvent?.Invoke(this, RCD);
            }
        }

        /// <summary>
        /// CallBack
        /// </summary>
        /// <param name="session"></param>
        /// <param name="key"></param>
        /// <param name="monitoredItem"></param>
        /// <param name="eventArgs"></param>
        private void SubCallBack(object session, string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs eventArgs)
        {
            ReturnChangeDataList RCD = new ReturnChangeDataList();

            MonitoredItemNotification notification = eventArgs.NotificationValue as MonitoredItemNotification;
            if (notification != null)
            {
                List<OpcChangeData> List = new List<OpcChangeData>();
                ChangeData cd = new ChangeData();
                RCD.session = session;
                RCD.key = key;
                cd.nodeid = monitoredItem.StartNodeId.ToString();
                if (notification.Value.WrappedValue.TypeInfo?.ValueRank == ValueRanks.OneDimension)
                {
                    Array array = notification.Value.WrappedValue.Value as Array;
                    int i = 0;

                    foreach (object obj in array)
                    {
                        OpcChangeData ocd = new OpcChangeData();
                        ocd.value = obj == null ? "" : obj.ToString();
                        List.Add(ocd);
                        i++;
                    }
                }
                else
                {
                    OpcChangeData ocd = new OpcChangeData();
                    ocd.value = notification.Value.WrappedValue.Value == null ? "" : notification.Value.WrappedValue.Value.ToString();
                    List.Add(ocd);
                }
                cd.List = List;
                RCD.ChangeData = cd;
                m_OpcChangeDataEvent?.Invoke(this, RCD);
            }
        }
        /// <summary>
        /// 订阅带Key的Nodes
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tags"></param>
        private void SubScriptionNode(string key, string[] tags)
        {
            uaClient.AddSubscription(websocketsession, key, tags, SubCallBack);
        }

        //}
        /// <summary>
        /// statuschange
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UaClient_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
        {
            m_OpcChangeStatusEvent?.Invoke(this, new ChangeStatusData() { session = websocketsession, message = JsonConvert.SerializeObject(GetJsonData("ChangeStatus", false, e.Text)) });
        }
        /// <summary>
        /// 连接完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UaClient_ReconnectComplete(object sender, EventArgs e)
        {
            m_OpcChangeStatusEvent?.Invoke(this, new ChangeStatusData() { session = websocketsession, message = JsonConvert.SerializeObject(GetJsonData("ChangeStatus", true, "重连成功...")) });
        }
        /// <summary>
        /// ReconnectStarting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UaClient_ReconnectStarting(object sender, EventArgs e)
        {
            m_OpcChangeStatusEvent?.Invoke(this, new ChangeStatusData() { session = websocketsession, message = JsonConvert.SerializeObject(GetJsonData("ChangeStatus", true, "开始重连...")) });
        }

        #endregion




        /// <summary>
        /// 读取
        /// </summary>
        /// <param name="Tags"></param>
        /// <returns></returns>
        public List<DataValue> ReadList(string[] Tags)
        {
            return uaClient.ReadNodes(Tags);

        }

        public string ReadNode(string tagName)
        {
            DataValue dataValue = uaClient.ReadNode(new NodeId(tagName));
            return ConvertValue(dataValue);
        }
        public (string, string) DeviceOnline(string tagName)
        {
            DataValue dataValue = uaClient.ReadNode(new NodeId(tagName));
            return (dataValue.ToString(), dataValue.StatusCode.ToString());
        }

        public List<string> ReadNodes(string[] tagNames)
        {
            List<DataValue> dataValueList = uaClient.ReadNodes(tagNames);
            return ConvertMoreValue(dataValueList);
        }
        public (List<string>, bool) ReadDates(string[] tagNames)
        {
            List<DataValue> date = uaClient.ReadNodes(tagNames);
            var res = date[0].StatusCode.ToString();
            bool allValuesAreEmpty = date.All(value => value.StatusCode.ToString() != "Bad");
            List<string> values = ConvertMoreValue(date);

            return (values, allValuesAreEmpty);
        }

        private List<string> ConvertMoreValue(List<DataValue> dataValueList)
        {
            List<string> listValue = new List<string>();
            foreach (DataValue dataValue in dataValueList)
            {
                listValue.Add(ConvertValue(dataValue));
            }
            return listValue;
        }

        public bool WriteNode<T>(string tagName, T value)
        {
            if (value != null)
            {
                return uaClient.WriteNode(tagName, value);
            }
            return false;
        }
        public List<string> HistoryReadRaw(string NodeId, DateTime startTime, DateTime endTime, uint cou, bool containBound)
        {
            try
            {
                var dataValueList = uaClient.ReadHistoryRawDataValues(NodeId, startTime, endTime, cou, containBound).ToList(); ;
                return ConvertMoreValue(dataValueList);
            }
            catch (Exception ex)
            {


                return null;
            }

        }
        private string ConvertValue(DataValue dataValue)
        {
            if (dataValue == null || dataValue.Value is null)
            {
                return "";
            }

            if (dataValue.WrappedValue.TypeInfo.ValueRank < 1)
            {
                return dataValue.Value.ToString();
            }
            string wrpValue = dataValue.WrappedValue.ToString();
            string[] array = wrpValue.Split(new Char[] { '{', '|', '}', ' ' }, StringSplitOptions.RemoveEmptyEntries);
            return string.Join(",", array);
        }
    }
}
